// +bulid windows

package main

import (
	"syscall"
	"unsafe"
)

type (
	SHORT int16
	DWORD uint32
	WORD  uint16
	BOOL  int32
	WCHAR uint16

	COORD struct {
		X SHORT
		Y SHORT
	}

	CONSOLE_SCREEN_BUFFER_INFO struct {
		Size              COORD
		CursorPosition    COORD
		Attributes        WORD
		Window            SMALL_RECT
		MaximumWindowSize COORD
	}

	SMALL_RECT struct {
		Left   SHORT
		Top    SHORT
		Right  SHORT
		Bottom SHORT
	}

	INPUT_RECORD struct {
		EventType WORD
		_         [2]byte
		Event     [16]byte
	}

	KEY_EVENT_RECORD struct {
		KeyDown         BOOL
		RepeatCount     WORD
		VirtualKeyCode  WORD
		VirtualScanCode WORD
		UnicodeChar     WCHAR
		ControlKeyState DWORD
	}
)

const (
	VK_ARROW_UP    = 0x26
	VK_ARROW_DOWN  = 0x28
	VK_ARROW_LEFT  = 0x25
	VK_ARROW_RIGHT = 0x27
)

var (
	Stdout  syscall.Handle
	Stdin   syscall.Handle
	tmp_arg DWORD
)

func (this SMALL_RECT) uintptr() uintptr {
	return uintptr(*(*int32)(unsafe.Pointer(&this)))
}

func (this COORD) uintptr() uintptr {
	return uintptr(*(*int32)(unsafe.Pointer(&this)))
}

func InitSyscall() {
	cout, err := syscall.Open("CONOUT$", syscall.O_RDWR, 0)
	if err != nil {
		panic(err)
	}
	Stdout = cout

	cin, err := syscall.Open("CONIN$", syscall.O_RDWR, 0)
	if err != nil {
		panic(err)
	}
	Stdin = cin

}

func GetConsoleCursorPosition() COORD {
	var tmp_info CONSOLE_SCREEN_BUFFER_INFO
	err := GetConsoleScreenBufferInfo(&tmp_info)
	if err != nil {
		panic(err)
	}
	return tmp_info.CursorPosition
}

func GetConsoleScreenBufferInfo(info *CONSOLE_SCREEN_BUFFER_INFO) (err error) {
	r0, _, e1 := proc_get_console_screen_buffer_info.Call(uintptr(Stdout), uintptr(unsafe.Pointer(info)))
	if int(r0) == 0 {
		if e1 != nil {
			err = e1
		} else {
			err = syscall.EINVAL
		}
	}
	return
}

func SetConsoleCursorPosition(pos COORD) (err error) {
	r0, _, e1 := proc_set_console_cursor_position.Call(uintptr(Stdout), pos.uintptr())
	if int(r0) == 0 {
		if e1 != nil {
			err = e1
		} else {
			err = syscall.EINVAL
		}
	}
	return
}

func ReadConsoleInput(record *INPUT_RECORD) (err error) {
	r0, _, e1 := proc_read_console_input.Call(uintptr(Stdin), uintptr(unsafe.Pointer(record)), 1, uintptr(unsafe.Pointer(&tmp_arg)))
	if int(r0) == 0 {
		if e1 != nil {
			err = e1
		} else {
			err = syscall.EINVAL
		}
	}
	return
}

func WaitForMultipleObjects(objects []syscall.Handle) (err error) {
	r0, _, e1 := syscall.Syscall6(proc_wait_for_multiple_objects.Addr(),
		4, uintptr(len(objects)), uintptr(unsafe.Pointer(&objects[0])),
		0, 0xFFFFFFFF, 0, 0)
	if uint32(r0) == 0xFFFFFFFF {
		if e1 != 0 {
			err = error(e1)
		} else {
			err = syscall.EINVAL
		}
	}
	return
}

var kernel32 = syscall.NewLazyDLL("kernel32.dll")
var (
	proc_get_console_screen_buffer_info = kernel32.NewProc("GetConsoleScreenBufferInfo")
	proc_set_console_cursor_position    = kernel32.NewProc("SetConsoleCursorPosition")
	proc_read_console_input             = kernel32.NewProc("ReadConsoleInputW")
	proc_wait_for_multiple_objects      = kernel32.NewProc("WaitForMultipleObjects")
)
